#include "aki/jsbind.h"
#include <hilog/log.h>
#include <random>
#include "hello.h"

// 模拟接收JS参数处理后返回数据
std::string SayHello(std::string msg)
{
    return msg + " too.";
}
// 模拟接收JS参数处理后异步返回数据
std::string AsyncSayHello(std::string msg)
{
    // 模拟耗时操作
    std::this_thread::sleep_for(std::chrono::milliseconds(5000));
    return msg + " SayHello2 too.";
}

// 触发C++调用JS函数
void StartProcess() {
    // 索引 JS 函数句柄
    auto jsFunc = aki::JSBind::GetJSFunction("sayHelloFromJS");
    // Invoke 指定 JS 方法的返回值类型，调用JS函数
    auto result = jsFunc->Invoke<std::string>("你好！我是 C++"); // 可在非JS线程执行
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, LOG_TAG, "JavaScript说： %{public}s", result.c_str());
}

// 操作ArrayBuffer类型函数，传入ArrayBuffer类型，返回ArrayBuffer类型
aki::ArrayBuffer PassingArrayBufferReturnArrayBuffer(aki::ArrayBuffer origin) {
    aki::ArrayBuffer buff(origin.GetData(), origin.GetCount());
    uint8_t* data = buff.GetData();
    data[4] = 4;
    data[5] = 5;
    data[6] = 6;
    data[7] = 7;

    return buff;
}

// 异步操作函数，将在新线程中执行
void doAsyncWork(const std::string& input, const std::string& onSuccess, const std::string& onFailed) {
    // 索引 JS 函数句柄
    auto jsSuccessFunc = aki::JSBind::GetJSFunction(onSuccess);
    auto jsFailedFunc = aki::JSBind::GetJSFunction(onFailed);
    // 模拟耗时操作
    std::this_thread::sleep_for(std::chrono::milliseconds(5000));
    
    // 生成随机数
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<int> dis(0, 1);
    int random_value = dis(gen);
    // 调用回调函数，将结果传递给ArkTS
    if (random_value == 0) {
        // 准备回调的参数
        std::string result = "C++处理成功: " + input;
        jsSuccessFunc->Invoke<std::string>(result); // 可在非JS线程执行
    } else {
        std::string result = "C++处理失败: " + input;
        jsFailedFunc->Invoke<std::string>(result); // 可在非JS线程执行
    }
}

// 定义C++类与ArkTs类映射
class TestObject {
public:
    double age;
    
    TestObject() = default;
    
    explicit TestObject(double value) {
        value_ = value;
    }
    
    ~TestObject() = default;
    
    static double MultiplyObject(TestObject obj1, TestObject obj2) {
        return obj1.value_ * obj2.value_;
    }
    
    double Multiply(double mult) {
        value_ *= mult;
        return value_;
    }
    // getter
    double GetMoney() const {
        return money_;
    }
    // setter
    void SetMoney(double money) {
        money_ = money;
    }
private:
    double value_ = 1;
    double money_;
};

// 定义类，模拟异步处理任务方法
class TaskRunner {
public:
    TaskRunner() = default;
    std::string DoTask() {
        // Do something;
        // 模拟耗时操作
        std::this_thread::sleep_for(std::chrono::milliseconds(5000));
        return "done.";
    }
};

// C++枚举
enum TypeFlags {
    NONE = 10,
    NUM = 20,
    STRING = 30,
    BUTT = -1
};

// 全局作用域
// Aki JSBind语法糖，注册 FFI 特性
JSBIND_GLOBAL() {
    // 把C++函数和ArkTs函数绑定
    JSBIND_FUNCTION(SayHello, "sayHello");
    // 被绑定的C++函数指针，当alias未被指定时，JavaScript与C++函数名相同
    // 使用JSBIND_PFUNCTION绑定 C++ 全局函数后，从 JavaScript 使用同Promise方式相同的异步调用。
    JSBIND_PFUNCTION(AsyncSayHello);
    JSBIND_FUNCTION(StartProcess);
    JSBIND_FUNCTION(PassingArrayBufferReturnArrayBuffer);
    JSBIND_PFUNCTION(doAsyncWork);
}

// 绑定类/结构体
// AKI 提供 JSBIND_CLASS 对 C++ 类/结构体进行绑定，在JSBIND_CLASS作用域下可绑定：类构造函数、类成员函数、类成员属性的类特性。
JSBIND_CLASS(TestObject) {
    // 绑定类构造函数：在JSBIND_CLASS作用域下使用绑定 C++ 类/结构体构造函数，其中为了支持多态，可通过类型模板指定构造函数参数类型。
    JSBIND_CONSTRUCTOR<>();
    // 绑定类有参构造函数
    JSBIND_CONSTRUCTOR<double>();
    // 绑定类成员函数：AKI 使用 JSBIND_METHOD 对C++ 的3种类成员函数进行绑定：类静态函数、类成员函数、const 类成员函数。
    JSBIND_METHOD(MultiplyObject);
    JSBIND_METHOD(Multiply);
    // 绑定类成员属性，必须是public
    JSBIND_PROPERTY(age);
    // 绑定field、getter、setter，c++类中是私有属性money_，暴露给ArkTs是money
    JSBIND_FIELD("money", GetMoney, SetMoney);
}
JSBIND_CLASS(TaskRunner) {
    // 绑定类无参构造函数
    JSBIND_CONSTRUCTOR<>();
    // 绑定异步调用方法
    JSBIND_PMETHOD(DoTask);
}
// 绑定枚举
JSBIND_ENUM(TypeFlags) {
    // 绑定枚举值
    JSBIND_ENUM_VALUE(NONE);
    JSBIND_ENUM_VALUE(NUM);
    JSBIND_ENUM_VALUE(STRING);
}

// 绑定模块，注册 AKI 插件名: 即为编译*.so名称，规则与NAPI一致
JSBIND_ADDON(hello)

